// 
//  MainWindow.cs
//  
//  Author:
//       BOUTRY Arnaud <lordphoenix@tuxfamily.org>
// 
//  Copyright (c) 2010 BOUTRY Arnaud as LordPhoenix <lordphoenix@tuxfamily.org>
// 
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//  
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// 
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting;
using System.Net;
using System.Threading;
using Gtk;
using Gdk;
using Mono.Unix;
using Notifications;

using swmt.swmtProvider;

namespace swmt
{
	public partial class winCloud : Gtk.Window
	{
		private StatusIcon trayicon;
		private ListStore lstServiceList;
		private ArrayList arlPluginList;
		private List<WebService> ServicesList;
		private string _accountlistfile;
		private Timer _tmr;
		
		private OptionList _options;
		//constants used for options names.
		private const string opt_refresh_delay="refresh_delay";
		private const string opt_show_notification="show_notification"; 
		private const string opt_Hide_on_close ="HideOnClose";
		private const string opt_Open_Window_on_start ="WindowOnStart";
		
		private bool nonotify = true; //Flag used on application start to prevent notification on start.
		
		private void RenderServiceName(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
		{
			WebService selected = (WebService)model.GetValue(iter,0);
			if (selected != null){
				string str = String.Format("{0} ({1})",selected.servicename,selected.accountName);
				(cell as CellRendererText).Text = str;
			}

		}
		private void RenderUnreadCount(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
		{
			WebService selected = (WebService)model.GetValue(iter,0);
			if (selected!=null){
				if(selected.Error){
					(cell as CellRendererText).Text =Catalog.GetString("Erreur");
					(cell as CellRendererText).ForegroundGdk = new Gdk.Color (0xff, 0, 0);
				}
				else{
					(cell as CellRendererText).Text =selected.LastCount.ToString();
					(cell as CellRendererText).ForegroundGdk = new Gdk.Color (0, 0, 0);
				}
			}
		}
		private void Close(){
			#region saving account configuration
			int idx = 1;
			FileStream fichier;
			if(!File.Exists(_accountlistfile))
				fichier = File.Open(_accountlistfile,FileMode.Create);
			else
				fichier = File.Open (_accountlistfile, FileMode.Truncate, System.IO.FileAccess.Write);
			StreamWriter w = new StreamWriter (fichier);
			w.BaseStream.Seek (0, SeekOrigin.Begin);
			string confdir = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/swmt/";
			foreach (WebService service in ServicesList){
				string accountoptionfile = idx.ToString () + "-" + service.servicename + ".conf";
				service.saveconfig (confdir+accountoptionfile);
				string tmpstring = idx.ToString()+"/"+service.pluginfile+"/"+accountoptionfile;
				w.WriteLine (tmpstring);
				idx++;
			}
			w.Flush ();
			fichier.Flush ();
			fichier.Close ();
			w.Close ();
			#endregion
			#region saving application option
			_options.Save();
			#endregion
			Application.Quit();
		}
		private void RefreshAccount ()
		{
			trayicon.Tooltip = Catalog.GetString ("Actualisation en cours…");
			statusbar1.Push (0, Catalog.GetString ("Actualisation en cours…"));
			Hashtable NotifyCount = new Hashtable ();
			int totalnew = 0;
			foreach(WebService service in ServicesList){
				try {
					if (service.connect ()) {
						int i = service.GetUnreadCount ();
						totalnew += i;
						if (i != 0)
							NotifyCount.Add (service.servicename, i);
					}
				}
				catch (WebException we ) {
					Console.WriteLine(we.Message);
				}
			}
			if (totalnew>0){
				trayicon.Tooltip=String.Format(Catalog.GetPluralString("{0} Nouvel élément"," {0} nouveaux élments",totalnew),totalnew);
				if(!nonotify && _options.getBool(opt_show_notification))
					ShowNotification(NotifyCount);
			}
			else{ 
				trayicon.Tooltip = Catalog.GetString("Pas de nouveaux éléments");
			}
			statusbar1.Pop(0);
			trvUnreadServices.QueueDraw();
			nonotify = false;
		}
		private void ShowNotification(Hashtable messageinfo){
			Notification alert = new Notification();
			alert.Summary = "SWMT";
			alert.Body = Catalog.GetString("Nouveaux éléments disponibles : ");
			foreach(DictionaryEntry entry in messageinfo){
				string messageline = String.Format("\n\t{0} pour : {1}.",entry.Value.ToString(),entry.Key.ToString());
				alert.Body += messageline;
			}
			alert.Urgency = Urgency.Normal;
			alert.Icon = new Gdk.Pixbuf(null,"swmt.img.swmt.png");
			alert.Show();
		}
		private void onTimerElapsed(object state){
			Gtk.Application.Invoke (delegate {
				this.RefreshAccount();
			});
		}
		protected void onTrayMenu(object sender, EventArgs args){
			Menu traymenu = new Menu();
			ImageMenuItem mnuQuit = new ImageMenuItem(Stock.Quit,null);
			ImageMenuItem mnuRefresh = new ImageMenuItem(Stock.Refresh,null);
			mnuRefresh.Activated += delegate{
				RefreshAccount();
			};
			ImageMenuItem mnuAbout = new ImageMenuItem(Stock.About,null);
			mnuAbout.Activated += OnBtnAboutClicked;
			mnuQuit.Activated += delegate {
				this.Close();
			};
			foreach(WebService service in ServicesList){
				string menuLabel = String.Format("{0} \t\t ({1})",service.servicename,service.LastCount.ToString());
				string icofilename = Environment.GetFolderPath(Environment.SpecialFolder.Personal)+
					"/.cache/swmt/"+service.servicename+"-ico.ico";
				ImageMenuItem mnuitService = new ImageMenuItem(menuLabel);
				if(File.Exists(icofilename)){
					Gtk.Image img = new Gtk.Image(new Pixbuf(icofilename));
					mnuitService.Image = img;
				}
				
				traymenu.Add(mnuitService);
				mnuitService.Activated += delegate {
					System.Diagnostics.Process.Start(service.Homepage);
				};
			}
			traymenu.Add(new SeparatorMenuItem());
			traymenu.Add(mnuRefresh);
			traymenu.Add(mnuAbout);
			traymenu.Add(mnuQuit);
			traymenu.ShowAll();
			traymenu.Popup();
		}
		public winCloud () : base(Gtk.WindowType.Toplevel)
		{
			Build ();
			arlPluginList = new ArrayList();
			ServicesList = new List<WebService>();
			Pixbuf img = new Pixbuf(null,"swmt.img.swmt.png");
			trayicon = new StatusIcon(img);
			trayicon.Visible = true;
			trayicon.Activate += iconClick ;
			trayicon.PopupMenu += onTrayMenu;
			Mono.Unix.Catalog.Init("swmt","");
			#region Graphical elements definition
			TreeViewColumn trcService = new TreeViewColumn();
			trcService.Expand = true;
			trcService.Title = Catalog.GetString("Service");
			CellRendererText celService = new CellRendererText();
			trcService.PackStart(celService,true);
			trcService.SetCellDataFunc(celService,new Gtk.TreeCellDataFunc(RenderServiceName));
			TreeViewColumn trcUnread  = new TreeViewColumn();
			trcUnread.FixedWidth = 20;
			trcUnread.Title = Catalog.GetString("Non lus");
			CellRendererText celUnread = new CellRendererText();
			trcUnread.PackStart(celUnread,true);
			trcUnread.SetCellDataFunc(celUnread,new Gtk.TreeCellDataFunc(RenderUnreadCount));

			trvUnreadServices.AppendColumn(trcService);
			trvUnreadServices.AppendColumn(trcUnread);

			lstServiceList = new ListStore (typeof(WebService));
			trvUnreadServices.Model = lstServiceList;
			trvUnreadServices.Selection.Changed += trvUnreadServicesSelectionChanged;
			#endregion
			#region account list loading.
			string confdir = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/swmt/";
			if (!Directory.Exists(confdir))
				Directory.CreateDirectory(confdir);
			_accountlistfile = confdir+"accounts.list";
			FileStream fichier;
			StreamReader read;
			string optionstring;
			if(File.Exists(_accountlistfile)){
				fichier = File.Open (_accountlistfile, FileMode.Open);
				read = new StreamReader (fichier);
				read.BaseStream.Seek (0, SeekOrigin.Begin);
				while (!read.EndOfStream) {
					optionstring = read.ReadLine();
					if(!optionstring.StartsWith("#")){
						int cpos = optionstring.IndexOf("/");
						int cpos2 = optionstring.IndexOf("/",cpos+1);
						string pluginfile = optionstring.Substring(cpos+1,cpos2-cpos-1);
						string paramfile = optionstring.Substring (cpos2+1, optionstring.Length - cpos2 - 1);
						string filename =GetPluginDir()+pluginfile;
						WebService account = null;
						Assembly ass = Assembly.LoadFrom(filename);
						PluginInfoAttribute[] attrlist = (PluginInfoAttribute[])ass.GetCustomAttributes(typeof(PluginInfoAttribute),true);
						if(attrlist.Length==1){
							string classename = "swmt.swmtProvider."+attrlist[0].ClassName;
							account= (WebService)ass.CreateInstance(classename,true);
							account.loadconfig(confdir+paramfile);
							lstServiceList.AppendValues(account);
							ServicesList.Add(account);
						}
					}
				}
				read.Close();
				fichier.Close();
			}

			#endregion
			#region Loading option file
			string _optionfilename;
			_optionfilename = confdir+"options.txt";
			if(File.Exists(_optionfilename)){
				_options = new OptionList(_optionfilename);
			}
			else{
				//If option file doesn't exist set default value for options
				_options = new OptionList();
				_options.set(opt_refresh_delay,10);
				_options.set(opt_show_notification,true);
				_options.set(opt_Hide_on_close,false);
				_options.set(opt_Open_Window_on_start,true);
				_options.Save(_optionfilename);
			}
			try {
				
				spbRefreshDelay.Value = _options.getInt(opt_refresh_delay);
				ckbNotfy.Active = _options.getBool(opt_show_notification);
				ckbHideOnClose.Active = _options.getBool(opt_Hide_on_close);
				ckbHideWindowStart.Active = !_options.getBool(opt_Open_Window_on_start);
			}
			//if trying to read an unknown option (in case of software update for exemple) define it to default value
			//and define application behaviour.
			catch(UndefinedOptionException e){
				if(e.OptionName==opt_refresh_delay){
					_options.set(opt_refresh_delay,10);
					spbRefreshDelay.Value = 10;
				}
				if(e.OptionName==opt_show_notification){
					_options.set(opt_show_notification,true);
					ckbNotfy.Active = true;
				}
				if(e.OptionName==opt_Hide_on_close){
					_options.set(opt_Hide_on_close,false);
					ckbHideOnClose.Active = false;
				}
				if(e.OptionName==opt_Open_Window_on_start){
					_options.set(opt_Open_Window_on_start,true);
					ckbHideWindowStart.Active = false;
				}
					
			}
			#endregion
			if (_options.getBool(opt_Open_Window_on_start))
				this.Show();
			int delay = _options.getInt(opt_refresh_delay)*60000;
			_tmr = new Timer(onTimerElapsed,null,delay,delay);
			RefreshAccount();
		}
		protected void trvUnreadServicesSelectionChanged(object sender, EventArgs args){
			statusbar1.Pop(1);
			TreeIter iter;
			trvUnreadServices.Selection.GetSelected(out iter);
			WebService service = (WebService)lstServiceList.GetValue(iter,0);
			if (service.Error)
				statusbar1.Push(1,service.ErrorMessage);
		}
		public void iconClick(object sender,EventArgs args){
			if(this.GdkWindow==null)
				this.Show();
			else if(this.GdkWindow.State==Gdk.WindowState.Iconified)
					this.Deiconify();
			else
				this.Visible=!this.Visible;
		}
		
		/// <summary>
		/// TODO: write a comment.
		/// </summary>
		private static string GetPluginDir ()
		{
			string asslocation=System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			int pos = asslocation.LastIndexOf(System.IO.Path.DirectorySeparatorChar);
			return asslocation.Substring(0,pos)+"/swmt-plugins/";
		}
		protected void OnDeleteEvent (object sender, DeleteEventArgs a)
		{
			a.RetVal = true;
			if(_options.getBool(opt_Hide_on_close))
				this.Hide();
			else
				this.Close();
			
		}
		protected virtual void OnBtnAddAccountClicked (object sender, System.EventArgs e){
			Menu mnuadd = new Menu();
			arlPluginList.Clear();
			string rundir = GetPluginDir()+"/";
			foreach(string pluginfilename in Directory.GetFiles(rundir,"*.dll")){
				Assembly ass = Assembly.LoadFrom(pluginfilename);
				PluginInfoAttribute[] attrlist = (PluginInfoAttribute[])ass.GetCustomAttributes(typeof(PluginInfoAttribute),true);
				if(attrlist.Length!=0){
					string _servicename = attrlist[0].ServiceName;
					string _icourl = attrlist[0].UrlFavicon;
					string cachedir = Environment.GetFolderPath(Environment.SpecialFolder.Personal)+"/.cache/swmt/";
					if (!Directory.Exists(cachedir))
						Directory.CreateDirectory(cachedir);
					string icofilename = cachedir+_servicename+"-ico.ico";
					if(!File.Exists(icofilename)){
						WebClient wcli = new WebClient();
						byte[] data = wcli.DownloadData(_icourl);
						FileStream fs = new FileStream(icofilename,FileMode.Create);
						fs.Write(data,0,data.Length);
						fs.Flush();
						fs.Close();
					}
					Pixbuf pxb = new Pixbuf(icofilename);
					int idx = arlPluginList.Add(pluginfilename);
					MenuItemData mnuService = new MenuItemData(_servicename,idx);
					Gtk.Image img = new Gtk.Image(pxb);
					mnuService.Image = img;
					mnuService.Activated += NewServiceItemActivated;
					mnuadd.Append(mnuService);
				}
			}
			mnuadd.ShowAll();
			mnuadd.Popup(null,null,null,0,Gtk.Global.CurrentEventTime);
		}
		protected virtual void NewServiceItemActivated(object sender,EventArgs args)
		{
			WebService newservice;
			int idx = (sender as  MenuItemData).index;
			string pluginfile = arlPluginList[idx].ToString();
			Assembly ass = Assembly.LoadFrom(pluginfile);
			PluginInfoAttribute[] attrlist = (PluginInfoAttribute[])ass.GetCustomAttributes(typeof(PluginInfoAttribute),true);
			if(attrlist.Length!=0){
				string ClasseName = "swmt.swmtProvider."+attrlist[0].ClassName;
				newservice = (WebService)ass.CreateInstance(ClasseName,true);
				ResponseType result = newservice.Prompt(false);
				if (result == ResponseType.Ok){
					lstServiceList.AppendValues(newservice);
					ServicesList.Add(newservice);
				}
			}
		}
		protected virtual void OnBtnDeleteAccountClicked (object sender, System.EventArgs e)
		{
			TreeIter iter;
			if (trvUnreadServices.Selection.GetSelected(out iter)){
				WebService service = (WebService)lstServiceList.GetValue(iter,0);
				service.deleteconfigfile();
				lstServiceList.Remove(ref iter);
				ServicesList.Remove(service);
			}
		}		
		protected virtual void OnBtnRefreshClicked (object sender, System.EventArgs e)
		{
			RefreshAccount();
		}
		protected virtual void OnBtnEditAccountClicked (object sender, System.EventArgs e)
		{
			TreeIter iter;
			if(trvUnreadServices.Selection.GetSelected(out iter)){
				WebService service = (WebService)lstServiceList.GetValue(iter,0);
				service.Prompt(true);
			}
		}
		protected virtual void OnSpbRefreshDelayValueChanged (object sender, System.EventArgs e)
		{
			_options.set(opt_refresh_delay,(int)spbRefreshDelay.Value);
			_tmr.Change(0,_options.getInt(opt_refresh_delay)*60000);
		}
		protected virtual void OnCkbNotfyToggled (object sender, System.EventArgs e)
		{
			_options.set(opt_show_notification,ckbNotfy.Active);
		}
		public virtual void OnBtnAboutClicked (object sender, System.EventArgs e)
		{
			AboutDialog dlg = new Gtk.AboutDialog();
			string[] authors = new String[]{"BOUTRY Arnaud <lordphoenix@tuxfamily.org>"};
			dlg.ProgramName ="Social Web Monitor Tool";
			dlg.Logo = new Gdk.Pixbuf(null,"swmt.img.logo.png");
			dlg.Comments=Catalog.GetString("Surveille vos services web préférés et vous prévient des nouveaux contenus");
			dlg.Version="0.2";
			dlg.Authors = authors;
			string[] artists = new string[]{Catalog.GetString("L'icone principale de SWMT vient de :" +
				" http://www.public-domain-photos.com"),Catalog.GetString("Le logo est de : ephase <ephase@xieme-art.org>")};
			dlg.Artists = artists;
			dlg.License=@"This program is free software; you can redistribute it and/or modify 
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
	
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
	
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n";
			dlg.Run();
			dlg.Hide();
		}
		protected virtual void OnCkbHideOnCloseToggled (object sender, System.EventArgs e)
		{
			_options.set(opt_Hide_on_close ,ckbHideOnClose.Active);
		}
		protected virtual void OnCkbHideWindowStartToggled (object sender, System.EventArgs e)
		{
			_options.set(opt_Open_Window_on_start,!ckbHideWindowStart.Active);
		}
	}
}
	
	

